OilTrack · ลำดับงานทางธุรกิจ (Business flow)
Prototype V.2 · 2026-09
Spheresoft Co., Ltd.
เอกสารอ้างอิงต้นแบบ — ไม่ใช่สัญญาส่งมอบ
OilTrack · ลำดับงานตั้งแต่ต้นจนจบ
เอกสารนี้เดินทีละเส้นทาง จากคนขับกดยื่นคำขอ ไปจนน้ำมันไหลออกหัวจ่าย สต็อกถูกตัด และต้นทุนลงบัญชี ทุกเส้นทางมีสามภาพประกอบ: ช่องทางของผู้เกี่ยวข้อง (swimlane) · ลำดับข้อความ (sequence) · ตารางที่ถูกเขียน (data-flow) เพื่อให้ทั้งฝ่ายธุรกิจและทีมพัฒนาอ่านฉบับเดียวกัน
อ่านคู่กับ Doc - Overview (โครงสร้างข้อมูลและสิทธิ์) และ Doc - Kiosk Gateway (สิ่งที่เกิดบนจอหัวจ่ายในแต่ละสถานะ)
เส้นทางทั้งหมดในเอกสารนี้
1 · เส้นทางปกติ — ยื่น → อนุมัติ → ผูก → จ่าย → ใบสรุป
5 · ธงระยะทางและการตรวจสอบ
2 · ไม่อนุมัติ และการยกเลิก
6 · รับเข้า-ตัดออก และการกระทบยอดสต็อก
3 · หมดอายุและการกวาดอัตโนมัติ
7 · บัญชีและต้นทุนต่อลิตร
4 · งานรถน้ำมันเคลื่อนที่และการเติมถัง
1 · เส้นทางปกติ
คนขับที่มีรถในความรับผิดชอบและไม่มีคำขอค้าง ยื่นคำขอหนึ่งใบ พนักงานบัญชีอนุมัติ คนขับไปที่หัวจ่าย สแกน QR ยืนยันในแอป แล้วน้ำมันจึงไหล จบด้วยใบสรุปที่หน้าจอหัวจ่ายและสถานะเสร็จสิ้นในแอป
1 ยื่นคำขอ
2 อนุมัติ
3 ผูกหัวจ่าย
4 จ่ายน้ำมัน
5 ปิดงาน
คนขับรถ
เลือกรถ · เลขไมล์ · เต็มถัง/ระบุลิตร · รับที่สถานีหรือหน้างาน · ยืนยัน
รอ — เห็นสถานะและเวลาที่เหลือ
สแกน QR (หรือกรอกรหัส 6 หลัก) → ยืนยันการผูกในแอป
ต่อหัวจ่ายเข้ารถ · ดูมาตรวัดที่จอตู้
เห็นสถานะ เสร็จสิ้น + ลิตรจริงในแอป
พนักงานบัญชี
คำขอเข้าคิว รายการที่รออนุมัติ
ตรวจ · แก้ลิตรที่อนุมัติได้ · กด อนุมัติ (หรือไม่อนุมัติ พร้อมเหตุผล)
เห็นคำขอย้ายไปกลุ่มกำลังดำเนินการ
เห็นมาตรวัดสดในรายการคำขอ
ต้นทุนและลิตรจริงเข้ารายงานทันที
ตู้คีออสก์
#1 idle — QR + รหัส
#1 idle
#2 binding (2 นาที) → #3 bound (5 นาที)
#4 progress — มาตรวัดสด
#5 complete (10 วิ) → #1
ระบบ
ล็อกรถ + คนขับ · ตั้งเวลา 240 นาที · บันทึกพิกัด
ตั้งเวลาอนุมัติใหม่ 120 นาที
จองหัวจ่าย · ตรวจรหัส TOTP ที่ส่วนกลาง · บันทึกพิกัดตอนสแกน
คุมเพดานลิตรเงียบ ๆ · เก็บร่องรอยทุก 2 วินาที
ตัดสต็อกแบบ FIFO · ปล่อยรถ คนขับ และหัวจ่าย
รูปที่ 1 — ช่องทางของผู้เกี่ยวข้องตลอดเส้นทางปกติ ช่องที่มีขอบซ้ายสีน้ำเงินคือช่องที่ผู้เกี่ยวข้องรายนั้นลงมือทำ ช่องอื่นคือรอหรือเพียงมองเห็น
ลำดับข้อความ (sequence)
#
จาก
ถึง
ข้อความ · การเรียก
1
แอปคนขับ
→
ส่วนกลาง
canRaiseRequest(phone, license) → raiseRequest(payload)
2
ส่วนกลาง
→
หลังบ้าน
คำขอสถานะ awaiting-review เข้าคิวรออนุมัติ (แจ้งเตือนข้ามหน้าจอ)
3
หลังบ้าน
→
ส่วนกลาง
approveRequest(id, litres) → สถานะ approved · ตั้ง expiresAt เป็น now + 120 นาที
4
ตู้คีออสก์
↔
ส่วนกลาง
แสดง QR + รหัสจาก dispenserTotp(id) หมุนทุก 30 วินาที
5
แอปคนขับ
→
ส่วนกลาง
resolveBind(id, code|qr) → beginBinding(...) · สถานะ binding · จองหัวจ่าย 2 นาที
6
แอปคนขับ
→
ส่วนกลาง
confirmBinding(id) → สถานะ bound · ตั้ง fuelStartDeadline เป็น now + 5 นาที
7
หัวจ่าย
→
ส่วนกลาง
startFueling(id) → สถานะ fueling · ล้าง fuelStartDeadline
8
หัวจ่าย
→
ส่วนกลาง
completeFueling(id, litres) → consumeForRequest(id) → เสร็จสิ้น
9
ส่วนกลาง
→
ทุกหน้าจอ
ปล่อยรถ/คนขับ/หัวจ่าย · เขียนรายการตัดสต็อก + ต้นทุน · แจ้งทุกแท็บ
รูปที่ 2 — ลำดับข้อความของเส้นทางปกติ ข้อ 5 และ 6 เป็นสองขั้นที่แยกกันโดยเจตนา: การสแกนเพียงจอง หัวจ่าย การยืนยันในแอปคือการผูก
ยื่นคำขอ
อนุมัติ
สแกน + ยืนยัน
จ่ายน้ำมัน
ปิดงาน
requests
+1 แถว · geoPoints[raise]
requests
status · approvedLitres · decidedBy/At · expiresAt
requests
dispenserId · nozzleNo · nozzleTankId · deadlines · geoPoints[bind]
requests
actualLitres (สด) · localStorage session marker
requests · stockRecords · stockLots · stockUsages · liveStock
dispensedAt · fuelCost · fuelUnitCost · geoPoints[pickup|delivery]
รูปที่ 3 — ตารางที่ถูกเขียนในแต่ละขั้น สังเกตว่าสต็อกถูกแตะเพียงขั้นเดียว: ตอนปิดงานด้วยลิตรที่จ่ายจริง ไม่ใช่ตอนอนุมัติ
2 · ไม่อนุมัติ และการยกเลิก
ทุกทางออกก่อนกำหนดไปรวมที่ฟังก์ชันเดียว releaseRequest() ซึ่งล้างการผูก กำหนดเวลา ผู้ขับรถน้ำมัน และช่องคำขอจัดส่งในการเขียนครั้งเดียว ผลคือรถและหัวจ่ายว่างพร้อมกันเสมอ ไม่มีสถานะกึ่งกลางที่รถยังถูกล็อกทิ้งไว้
ใครสั่ง
คนขับกดยกเลิก (เฉพาะคำขอของตน) · บัญชีกดไม่อนุมัติ / ยกเลิกพร้อมเหตุผล · ระบบกวาดหมดอายุ
→
releaseRequest(id, {status, reason, reasonBy})
ทางออกทางเดียวที่คำขอจะปิดเรื่อง
→
ผลในการเขียนครั้งเดียว
สถานะปลายทาง + เหตุผลและผู้สั่ง · ปล่อยรถ คนขับ หัวจ่าย · ล้าง expiresAt และช่องงานจัดส่ง · ไม่ตัดสต็อก
รูปที่ 4 — ทางออกก่อนกำหนดทั้งหมดใช้เส้นทางเดียวกัน คนขับยื่นคำขอใหม่ได้ทันทีหลังยกเลิก ไม่ต้องรอรอบใด
| กรณี |
ทำได้ที่สถานะ |
ต้องมีเหตุผล |
คนขับเห็นอะไร |
| คนขับยกเลิกเอง | รออนุมัติ · รอมอบหมายงาน · อนุมัติแล้ว | เลือกจากรายการ + หมายเหตุ | สถานะ ยกเลิก พร้อมเหตุผลของตัวเอง |
| บัญชีไม่อนุมัติ | รออนุมัติ | ใช่ — บังคับ | สถานะ ไม่อนุมัติ + เหตุผลจากบัญชี |
| บัญชียกเลิกกลางทาง | ทุกสถานะที่ยังมีชีวิต | ใช่ — บังคับ | สถานะ ยกเลิก + ผู้สั่ง · ถ้ากำลังผูกอยู่ จอตู้ขึ้น #6 |
| ปลดหัวจ่าย (ไม่ยกเลิกคำขอ) | ผูกหัวจ่ายแล้ว | ใช่ | กลับเป็น อนุมัติแล้ว · ผูกใหม่ได้ · เวลาอนุมัติเริ่มใหม่ |
ตารางที่ 1 — การกระทำที่ทำให้คำขอถอยหรือปิด ทุกครั้งที่ระบบปฏิเสธจะได้เหตุผลกลับมาทาง DB.lastBlock และหน้าจอต้องแสดงข้อความนั้น ไม่ใช่ปฏิเสธเงียบ
3 · หมดอายุและการกวาดอัตโนมัติ
คำขอที่ไม่มีใครแตะจะไม่ค้างล็อกรถไว้ตลอดไป การกวาด (DB.sync()) ทำงานตอนโหลดหน้า ทุก 60 วินาทีในหน้าที่เปิดอยู่ และหลังปิดงานทุกครั้ง เรียกซ้ำได้ไม่เปลี่ยนผล
รออนุมัติ · 240 นาที
หมดเวลา →expiredปล่อยรถ + คนขับ
อนุมัติแล้ว · 120 นาที
หมดเวลา →expiredปล่อยรถ + คนขับ
รอยืนยันการผูก · 2 นาที
หมดเวลา →approved (ถอยกลับ)ปลดหัวจ่าย · เวลาอนุมัติเริ่มใหม่
ผูกหัวจ่ายแล้ว · 5 นาที
หมดเวลา →cancelledไม่ได้เริ่มเติมน้ำมัน · ปลดหัวจ่าย
กำลังเติมน้ำมัน
ไม่ถูกกวาดเลยฮาร์ดแวร์กำลังทำงาน — ปิดได้ด้วยการจ่ายจบ หรือหยุดจ่ายครบ 1 นาที เท่านั้น
รูปที่ 5 — ผลของการหมดเวลาต่างกันตามสถานะ ความต่างที่สำคัญที่สุด: binding ถอยกลับได้เพราะยังไม่จับฮาร์ดแวร์ แต่ bound ต้องปิดเรื่องเพราะถือหัวจ่ายไว้แล้ว
การกวาดยังทำอีกสองอย่างในรอบเดียวกัน: ปิดคำขอจัดส่ง ที่คำขอในนั้นจบหรือถูกปล่อยหมดแล้ว (พร้อมยกเลิกคำขอเติมถังที่พ่วงอยู่ เพื่อไม่ให้ค้างจับรถและคนขับ) และ เขียนเครดิตน้ำมันเข้าถังเคลื่อนที่ เมื่อคำขอเติมถังเสร็จสิ้น ทุกการกระทำถูกบันทึกใน sweepLog ย้อนหลัง 50 รายการ ดูได้ที่ Tool - Database
4 · งานรถน้ำมันเคลื่อนที่และการเติมถัง
เมื่อคนขับเลือก “รับน้ำมันที่หน้างาน” คำขอไม่ไปที่หัวจ่ายประจำสถานี แต่เข้าคิวรอมอบหมายงาน พนักงานบัญชีรวมคำขอหลายใบเข้ารถน้ำมันเคลื่อนที่หนึ่งคัน ระบุลิตรสุดท้ายที่จะเติมใส่ถังบนรถ แล้วส่งงาน
1 เข้าคิว
2 รวมงาน
3 เติมถังบนรถ
4 ออกหน้างาน
คนขับที่ขอ
ยื่นคำขอ เลือกหน้างาน → รอมอบหมายงาน
เห็นว่ากำลังจัดรอบ
เห็นสถานะ อนุมัติแล้ว + ชื่อคนขับรถน้ำมัน
รับน้ำมันที่หน้างาน · ปิดงานที่หัวจ่ายบนรถ
พนักงานบัญชี
เห็นคิวรอมอบหมาย
สร้างคำขอจัดส่ง · เลือกรถ + คนขับรถน้ำมัน · ผูกคำขอเข้ารอบ · ปรับลิตรสุดท้าย (เตือนเมื่อเกินความจุถัง)
เห็นคำขอเติมถังที่ระบบสร้างให้ + รายการปรับสต็อกที่จะเกิด
ติดตามรอบจนปิด
คนขับรถน้ำมัน
—
ถูกเลือกเข้ารอบ
ได้คำขอเติมถัง (top-up) ในแอปของตน · ผูกหัวจ่ายที่สถานีและเติมเข้าถังบนรถ
ขับออกหน้างาน · จ่ายน้ำมันให้รถแต่ละคันตามคำขอในรอบ
ระบบ
ล็อกรถและคนขับที่ขอไว้ตามปกติ
คำนวณผลรวมลิตร · เตือนเกินความจุ (dispatchHeadroom)
สร้างคำขอ top-up (0 ลิตร = ไม่สร้าง) · เริ่มเวลาอนุมัติใหม่ทุกใบ ให้หมดพร้อมกัน
ถังประจำสถานีถูกตัดตอน top-up เสร็จ · ถังบนรถได้เครดิตพร้อมต้นทุนที่ตัดมา
รูปที่ 6 — งานหนึ่งรอบของรถน้ำมันเคลื่อนที่ จุดที่พลาดกันบ่อยคือขั้นที่ 3: การเติมถังบนรถเป็นคำขอปกติที่ยิงที่หัวจ่ายประจำสถานี ไม่ใช่รายการโอนย้ายที่คนกรอกเอง
ไม่มีการบันทึกซ้อน: น้ำมันหนึ่งลิตรถูกตัดจากถังประจำสถานีครั้งเดียว (ตอน top-up เสร็จ) และไปปรากฏเป็นเครดิตของถังบนรถในรายการเดียวกันแบบอัตโนมัติ เมื่อรถไปจ่ายให้เครื่องจักรที่หน้างาน จึงตัดจากถังบนรถ ไม่ใช่จากถังสถานีอีกรอบ
5 · ธงระยะทางและการตรวจสอบ
ระบบเก็บพิกัดสี่จุดต่อคำขอ และเทียบกับจุดอ้างอิงของเหตุการณ์นั้นเอง เกินรัศมี 500 เมตร คำขอถูกตั้งธงให้บัญชีตรวจ — ไม่บล็อกคนขับ ไม่แจ้งคนขับ และไม่มีผลต่อการจ่ายน้ำมัน ระยะทางคำนวณสด ไม่เก็บค่าไว้
| เหตุการณ์ |
เก็บเมื่อ |
เทียบกับ |
| raise | คนขับกดยืนยันคำขอ | สถานีหรือหน้างานที่ใกล้ที่สุด |
| bind | สแกนผูกหัวจ่าย | สถานีของหัวจ่ายนั้น — ถ้าหัวจ่ายอยู่บนรถ ใช้หน้างานของคำขอ |
| pickup | จ่ายเสร็จ (คำขอแบบรับที่สถานี) | สถานีของหัวจ่ายที่ผูก |
| delivery | จ่ายเสร็จ (คำขอแบบรับที่หน้างาน) | หน้างานที่ระบุในคำขอ |
ตารางที่ 2 — สี่จุดพิกัดและจุดอ้างอิงของแต่ละจุด คำขอเก่าที่ยังไม่มี geoPoints ถอยไปใช้พิกัดตอนผูกที่เก็บไว้แบบเดิมได้
รอตรวจสอบ
ธงขึ้นแล้ว ยังไม่มีคำตัดสิน · นับอยู่ในป้ายสีแดงบนเมนู
→
บัญชีตรวจตามลำดับ
โทรถามคนขับ · ขอภาพกล้องวงจรปิด · เทียบมาตรวัด (ระบบไม่มีคลิปให้ดู)
→
น่าสงสัย
ไม่พบความผิดปกติ
ตัดสินได้ครั้งเดียว ล็อกทันที ต้องมีเหตุผล
รูปที่ 7 — ขั้นตอนการตรวจสอบธง ผลการตรวจไม่ย้อนกลับไปเปลี่ยนสถานะคำขอ ไม่บล็อกอะไร และคนขับไม่เห็นทั้งกระบวนการ
6 · รับเข้า-ตัดออก และการกระทบยอดสต็อก
สต็อกมีสามชนิดรายการ และชนิดรายการเป็นตัวกำหนดทิศทาง ไม่มีช่องให้เลือกบวก-ลบในหน้าจอ ประเภทการปรับปรุงถูกจำกัดตามชนิดรายการ เช่น ซื้อเข้าเลือกได้เฉพาะฝั่งเพิ่ม ขายออกเฉพาะฝั่งลด
incoming · รับเข้าสต็อก
น้ำมันเข้าถัง ต้องมีราคาต่อลิตร → เปิดล็อตใหม่ ไม่ต้องเลือกประเภทการปรับปรุง
add · เพิ่มสต็อก
ซื้อเข้า · ตรวจนับสต็อก · โอนย้ายภายใน · ปรับปรุงบัญชี · อื่นๆ → เปิดล็อตใหม่
deduct · ลดสต็อก
ขายออก · ตรวจนับ · โอนย้าย · รั่วไหล/สูญเสีย · การระเหย · ปรับปรุงบัญชี · อื่นๆ → ตัดจากคิว FIFO
รูปที่ 8 — สามชนิดรายการ การจ่ายตามคำขอเป็นชนิด deduct ที่ระบบเขียนเองและซ่อนจากตัวเลือกในฟอร์ม เพื่อไม่ให้คนบันทึกซ้ำกับที่หัวจ่ายทำไปแล้ว
การกระทบยอดคือการเทียบสิ่งที่ควรมีกับสิ่งที่นับได้จริง: บัญชีนับน้ำมันในถัง แล้วบันทึกด้วยประเภท ตรวจนับสต็อก (นับจริง) ฝั่งเพิ่มหรือลดตามผลต่าง ระบบไม่แก้ประวัติย้อนหลัง — ส่วนต่างถูกบันทึกเป็นรายการใหม่พร้อมเหตุผล จึงตรวจสอบย้อนกลับได้ทั้งเส้น และค่าคงที่ รับเข้า − จ่ายออก = คงเหลือ เป็นจริงตลอดเวลาโดยโครงสร้าง
7 · บัญชีและต้นทุนต่อลิตร
ต้นทุนไม่ได้มาจากราคาซื้อครั้งล่าสุด แต่มาจากล็อตที่ถูกตัดจริงในการจ่ายครั้งนั้น เส้นทางของตัวเลขหนึ่งตัวจึงเดินจากใบสั่งซื้อไปจนถึงรายงานต้นทุนต่อคันได้ทั้งเส้น
1 รับเข้า
4,000 ล. @ ฿30.20
→ LOT-0022
2 จ่ายจริง
70.00 ล. ที่ DP-0001
มาตรวัดของหัวจ่าย
3 ปันส่วน FIFO
40 ล. @ ฿29.50 + 30 ล. @ ฿30.20
→ SU-0121
4 ลงที่คำขอ
฿2,086.00 · ฿29.80/ล.
fuelCost · fuelUnitCost
5 รายงาน
ต้นทุนต่อคัน · ต่อหน้างาน · ต่อสัปดาห์
คำนวณสด ไม่มีตารางสรุป
รูปที่ 9 — เส้นทางของต้นทุนหนึ่งก้อน ทุกขั้นเก็บบริบทของคำขอไว้กับแถวนั้น (ทะเบียนรถ ชื่อคนขับ หน้างาน หัวจ่าย) รายงานจึงยังอ่านได้ถูกแม้รถหรือคนขับถูกลบไปแล้ว
| กรณีพิเศษ |
ระบบทำอะไร |
บัญชีต้องทำอะไร |
| สต็อกในระบบไม่พอแต่จ่ายไปแล้ว | จ่ายได้ · ส่วนเกินคิดด้วยราคาล่าสุดที่รู้ · ตั้งธง ต้นทุนไม่ครบ | บันทึกรับเข้าย้อนหลังให้ครบ แล้วต้นทุนจะถูกคิดใหม่ |
| คำขอจบด้วยข้อผิดพลาด | ตัดสต็อกเท่าที่จ่ายไปแล้วจริง (บางส่วน) | ตรวจมาตรวัดจริงกับตัวเลขในระบบ · บันทึกหมายเหตุ |
| ตัดสต็อกผิด ต้องคืน | reverseUsage() คืนลิตรกลับหัวล็อตเดิมที่ตัดมา | ระบุเหตุผล — ประวัติเดิมไม่ถูกลบ |
| น้ำมันบนรถเคลื่อนที่ | ล็อตบนรถได้ต้นทุนจากการจ่ายที่สถานี ไม่ใช่ ฿0 | ไม่ต้องทำอะไร — ตรวจได้ที่หน้าสต็อกปัจจุบัน |
ตารางที่ 3 — กรณีที่ต้นทุนไม่ตรงไปตรงมา ทุกกรณีเลือกที่จะบันทึกความจริงพร้อมธง มากกว่าปฏิเสธการจ่ายน้ำมันหน้างาน